﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Agility.Web.Services;
using System.ServiceModel.DomainServices.Server;
using Moq;
using Agility.Web.Models.DomainModel;
using Agility.Web.Repositories;
using Agility.Web.Services.RepositoryDomainServicePattern;
using Microsoft.ServiceModel.DomainServices.Server.UnitTesting;
using Agility.TestProject.AppDriver;
using System.Security.Principal;
using Agility.TestProject.WebAii;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace Agility.Testing.Web.Requirements
{
    [TestFixture]
    public class RequirementsManagementServiceTests
    {
        AuthenticationServiceReference.AuthenticationServiceSoapClient authenticationClient;
        DomainServiceTestHost<RequirementsManagementService> target;
        Mock<IEpicRepository> mockEpicRepository;
        List<Epic> epics;
        string authenticationCookie;
        bool loggedIn;
        AppDriverBase appDriverBase;

        Mock<IPrincipal> mockPrincipal;

        [TestFixtureSetUp]
        public void SetupTestFixture()
        {
            appDriverBase = new AppDriverBase();
            string username = appDriverBase.CreateUser();

            // http://blogs.msdn.com/b/davrous/archive/2010/12/03/how-to-open-a-wcf-ria-services-application-to-other-type-of-clients-the-soap-endpoint-3-5.aspx

            authenticationClient = new AuthenticationServiceReference.AuthenticationServiceSoapClient();
            using (new OperationContextScope(authenticationClient.InnerChannel))
            {

                AuthenticationServiceReference.QueryResultOfUser qryUser = authenticationClient.Login(username, "Test##100", false, string.Empty);

                HttpResponseMessageProperty response = (HttpResponseMessageProperty)OperationContext.Current.IncomingMessageProperties[HttpResponseMessageProperty.Name];
                this.authenticationCookie = response.Headers["Set-Cookie"];
                this.loggedIn = true;
            }
            mockPrincipal = new Mock<IPrincipal>();
            Mock<IIdentity> mockIdentity = new Mock<IIdentity>();
            mockIdentity.SetupGet(i => i.AuthenticationType).Returns("Forms");
            mockIdentity.SetupGet(i => i.IsAuthenticated).Returns(true);
            mockIdentity.SetupGet(i => i.Name).Returns(username);
            mockPrincipal.SetupGet(p => p.Identity).Returns(mockIdentity.Object);
            mockPrincipal.Setup(p => p.IsInRole("RegisteredUser")).Returns(true);
        }

        [TestFixtureTearDown]
        public void TeardownFixture()
        {
            authenticationClient.Logout();
            this.authenticationCookie = string.Empty;
            this.loggedIn = false;
            appDriverBase.Cleanup();
        }

        [SetUp]
        public void SetupTest()
        {
            epics = new List<Epic>();
            this.mockEpicRepository = new Mock<IEpicRepository>();
            this.mockEpicRepository.Setup(r => r.Insert(It.IsAny<object>())).Callback((object o) => epics.Add((Epic)o));
            this.mockEpicRepository.Setup(r => r.GetEntities()).Returns(epics.Where(e => e.RequestedBy == this.mockPrincipal.Object.Identity.Name).AsQueryable());
            this.mockEpicRepository.Setup(r => r.Update(It.IsAny<object>(), It.IsAny<object>())).Callback((object u, object o) => epics[0] = (Epic)u);
            target = new DomainServiceTestHost<RequirementsManagementService>(this.CreateDomainService, mockPrincipal.Object);
        }

        [TearDown]
        public void TeardownTest()
        {
            this.mockEpicRepository = null;
            epics = null;
            target = null;
        }

        #region UnitTests
        
        [Test]
        [Category("UnitTest")]
        public void TestRequiresAuthenticationAttribute()
        {
            var attribute = typeof(RequirementsManagementService).GetCustomAttributes(typeof(RequiresAuthenticationAttribute), false)
                                                                  .Cast<RequiresAuthenticationAttribute>()
                                                                  .FirstOrDefault();
            Assert.IsNotNull(attribute, "RequiresAuthenticationAttribute is not set on RequirementsManagementService");
        }

        #endregion

        #region ComponentTests

        public RequirementsManagementService CreateDomainService()
        {
            return new RequirementsManagementService(this.mockEpicRepository.Object);
        }

        [Test]
        [Category("ComponentTest")]
        public void TestGetMyIdeas()
        {
            string username = this.mockPrincipal.Object.Identity.Name;
            // Arrange
            Epic e1 = Epic.CreateEpic(Guid.NewGuid(), "First Epic", "This is my first epic", DateTime.Today, username);
            epics.Add(e1);
            Epic e2 = Epic.CreateEpic(Guid.NewGuid(), "Second Epic", "This is my second epic", DateTime.Today, username);
            epics.Add(e2);
            Epic e3 = Epic.CreateEpic(Guid.NewGuid(), "Third Epic", "This is someone else's epic", DateTime.Today, "Frank");
            epics.Add(e3);
            int expected = mockEpicRepository.Object.GetEntities().Count();

            // Act
            IEnumerable<Epic> actual = this.target.Query(ds => ds.GetMyIdeas());

            //Assert
            mockEpicRepository.Verify(r => r.GetEntities());
            Assert.AreEqual(expected, actual.Count(), "GetMyIdeas does not return all my ideas");
            Assert.AreEqual(2, actual.Count(), "GetMyIdeas does not return 2 epics");
        }

        [Test]
        [Category("ComponentTest")]
        public void TestAddIdea()
        {
            // Arrange
            Epic expected = Epic.CreateEpic(Guid.NewGuid(), "My new Idea", "This is my new idea", DateTime.Today, this.mockPrincipal.Object.Identity.Name);

            // Act
            this.target.Insert<Epic>(expected);

            // Assert
            this.mockEpicRepository.Verify(r => r.Insert(It.Is<object>(e => ((Epic)e).Id == expected.Id)), Times.Once());
            Assert.AreEqual(1, epics.Count, "AddEpic results in a wrong number of epics in the repository");
            Assert.AreEqual(expected.Id, epics[0].Id, "AddEpic messes with the epic it adds, the Id is different");
        }

        [Test]
        [Category("ComponentTest")]
        public void TestUpdateIdea()
        {
            // Arrange
            Guid id = Guid.NewGuid();
            Epic original = Epic.CreateEpic(id, "An idea", "Some description of the idea", DateTime.Today, this.mockPrincipal.Object.Identity.Name);
            epics.Add(original);
            string expectedTitle = "Updated title";
            string extectedDescription = "Updated description";

            // Act
            Epic updated = target.Query(ds => ds.GetMyIdeas()).Single(e => e.Id == id);
            updated.Title = expectedTitle;
            updated.Description = extectedDescription;
            target.Update<Epic>(updated);

            // Assert
            this.mockEpicRepository.Verify(r => r.Update((object)updated, It.IsAny<object>()), Times.Once());
            Assert.AreEqual(updated.Id, epics[0].Id, "Wrong epic");
            Assert.AreEqual(updated.Title, epics[0].Title, "Title was not updated correctly");
            Assert.AreEqual(updated.Description, epics[0].Description, "Description was not updated correctly");
        }

        //[Test]
        //[Category("UnitTest")]
        //public void TestGetEpics()
        //{
        //    // Arrange
        //    Epic e1 = new Epic();
        //    e1.Description = "This is the first epic";
        //    e1.Title = "First Epic";
        //    epics.Add(e1);
        //    Epic e2 = new Epic();
        //    e2.Title = "Second Epic";
        //    e2.Description ="This is the second epic";
        //    epics.Add(e2);
        //    this.mockEpicRepository.Setup(r => r.GetEntities()).Returns(epics.AsQueryable());
        //    int expected = mockEpicRepository.Object.GetEntities().Count();
        //    target = new DomainServiceTestHost<RequirementsManagementService>(this.CreateDomainService, mockPrincipal.Object);

        //    // Act
        //    IEnumerable<Epic> actual = this.target.Query(ds => ds.GetEpics());

        //    //Assert
        //    Assert.AreEqual(expected, actual.Count(), "GetEpics does not return all epics");
        //    Assert.AreEqual(2, actual.Count(), "GetEpics does not return 2 epics");
        //}
        #endregion
    }
}
